使用 Helm 与 Kustomize 实践声明式管理
Kubernetes 于 2014 年夏季登陆 GitHub ,2015 年夏季推出 Kubernetes 的第一个 1.0 版本,其发展速度和进步在许多人的脑海中仍然记忆犹新。
像任何应用基础设施一样,workloads 的操作开始在 Kubernetes 上变得越来越重要。所以 Kubernetes 提出了 resources 的概念,通常由基于 YAML 的 manifests 来描述各种各样的 workloads,如 deployment.yaml。
随着需要部署的 manifests 数量和需要维护的 Kubernetes 集群数量的双重增加,我们需要更高效的 manifests 管理方式,以便在 cluster-to-cluster 之间获得更容易的部署,和更灵活的配置。
本文将介绍如何使用 Heml 和 Kustomize 来对 Kubernetes 生态系统内的对象进行声明式配置和包管理。
什么是 Helm ?
Helm 是 Kubernetes 的应用程序包管理器,您可使用 Helm Charts 描述应用程序的结构,使用 Helm 命令行界面,您可以回滚部署、监控应用程序的状态并跟踪每项部署的历史记录。
Helm 最初由Deis于 2015 年开发,并在首届 Kubecon 上展出。2019 年 4 月,CNCF 宣布 Helm 的孵化期结束,成为一个完整的项目。
Tiller是 Helm v2 平台的一部分。它来自与 Google 的 Deployment Manager 项目集成的部分,旨在成为一个工 作运行器,与Cloud Foundry 的 Bosh没有太大区别。简而言之,Tiller 是 Helm 的集群内部分,它运行 Helm 的命令和图表。由于 Tiller 可以不受限制地访问 Kubernetes 集群成为集群安全的痛点,因此在 Helm V3 中将其删除了。
基本概念
- Chart:Helm 使用的包格式称为 chart。 chart 就是一个描述Kubernetes相关资源的文件集合。
- Release:在 Kubernetes 集群上运行的 Chart 的一个实例。在同一个集群上,一个 Chart 可以安装很多次。每次安装都会创建一个新的 release。
- Repository:用于发布和存储 Chart 的存储库。
Helm Charts
- Helm 图表在创建时必须具有遵循语义版本控制的版本号。
- Helm Charts 可以引用其他 Charts 作为依赖项,这是任何包管理器的核心。
- Helm Charts 的更多高级功能如Chart Hooks和Chart Tests,它们允许与 Release 的生命周期进行交互,以及分别针对 Chart 运行命令/测试的能力。
运行 helm create eg-Helm
创建一个 Helm Charts
可以看到 Chart 的基本目录结构包括:
eg-Helm/ # chart 包目录名
├── charts # 包含 chart 依赖的其他 chart
├── Chart.yaml # 包含了chart信息的YAML文件,如chart的名字,版本号信息。
├── crds # 自定义资源的定义
├── templates # 模板目录,和 values 结合时,可生成有效的 Kubernetes manifest
│ ├── deployment.yaml
│ ├── _helpers.tpl # helm视为公共库定义文件,主要用于定义通用的子模版、函数等
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt # 帮助信息文件,helm install 安装成功后会输出帮助信息
│ ├── serviceaccount.yaml
│ ├── service.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml # chart 默认的配置值,模版可以引用这里参数。
简单示例
下面给出了 deployment、service、ingress 三个配置文件。
- Deployment
- Service
- Ingress
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: myapp #deployment应用名
labels:
app: myapp #deployment应用标签定义
spec:
replicas: 1 #pod副本数
selector:
matchLabels:
app: myapp #pod选择器标签
template:
metadata:
labels:
app: myapp #pod标签定义
spec:
containers:
- name: myapp #容器名
image: xxxxxx:1.7.9 #镜像地址
ports:
- name: http
containerPort: 80
protocol: TCP
apiVersion: v1
kind: Service
metadata:
name: myapp-svc #服务名
spec:
selector: #pod选择器定义
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 80
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: myapp-ingress #ingress应用名
spec:
rules:
- host: www.xxxxx.com #域名
http:
paths:
- path: /
backend:
serviceName: myapp-svc #服务名
servicePort: 80
提取 k8s 应用部署配置文件中的参数,作为 chart 包参数。
{{ }}
两个花括号包裹的内容为模版表达式。
- Deployment
- Service
- Ingress
- values
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: {{ .Release.Name }} #deployment应用名
labels:
app: {{ .Release.Name }} #deployment应用标签定义
spec:
replicas: {{ .Values.replicas}} #pod副本数
selector:
matchLabels:
app: {{ .Release.Name }} #pod选择器标签
template:
metadata:
labels:
app: {{ .Release.Name }} #pod标签定义
spec:
containers:
- name: {{ .Release.Name }} #容器名
image: {{ .Values.image }}:{{ .Values.imageTag }} #镜像地址
ports:
- name: http
containerPort: 80
protocol: TCP
apiVersion: v1
kind: Service
metadata:
name: {{ .Release.Name }}-svc #服务名
spec:
selector: #pod选择器定义
app: {{ .Release.Name }}
ports:
- protocol: TCP
port: 80
targetPort: 80
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: {{ .Release.Name }}-ingress #ingress应用名
spec:
rules:
- host: {{ .Values.host }} #域名
http:
paths:
- path: /
backend:
serviceName: {{ .Release.Name }}-svc #服务名
servicePort: 80
#域名
host: www.XXX.com
#镜像参数
image: XXXXXXXXXXXXXXXXXX
imageTag: 1.7.9
#pod 副本数
replicas:1
Helm 模版语法
表达式
- 模版表达式:
{{ 模版表达式 }}
{{- 模版表达式 -}}
表示去掉表达式输出结果前面和后面的空格{{- 模版表达式 }}
表示去掉前面空格{{ 模版表达式 -}}
表示去掉后面空格
变量
默认情况点( . ), 代表全局作 用域,用于引用全局对象。
helm 全局作用域中有三个重要的全局对象:Values 、 Release 和 自定义模版变量
-
Values 代表的就是 values.yaml 定义的参数,通过.Values可以引用任意参数。
例如:
{{ .Values.images.repository }}
引用了全局作用域下的 Values 中的 image 对象的 repository。values.yamlimages:
repository: gcr.io -
Release 作为 Helm 内置变量为我们提供了如下的属性字段:
Release.Name # release 名字,一般通过Chart.yaml定义,或者通过helm命令在安装应用的时候指定。
Release.Time # release 安装时间
Release.Namespace # 命名空间
Release.Revision # release 版本号,是一个递增值,每次更新都会加一
Release.IsUpgrade # true 代表,当前release是一次更新
Release.IsInstall # true 代表,当前release是一次安装 -
自定义模版变量
自定义模版变量名以 $ 开头, 赋值运算符为
:=
例如:
{{- $relname := .Release.Name -}}
函数 & 管道运算符
Go templates 为我们提供了很多好用的函数,详见文档
-
调用函数的语法:
{{ functionName arg1 arg2... }}
-
管道(pipelines)运算符
|
,用于将模版输出的结果传递给下一个函数处理。例子如下:
-
将.Values.favorite.food 传递给 quote 函数处理,然后在输出结果:
{{ .Values.favorite.food | quote }}
-
先将.Values.favorite.food的值传递给upper函数将字符转换成大写,然后专递给quote加上引号包括起来:
{{ .Values.favorite.food | upper | quote }}
-
如果.Values.favorite.food为空,则使用default定义的默认值:
{{ .Values.favorite.food | default "默认值" }}
-
将.Values.favorite.food输出5次:
{{ .Values.favorite.food | repeat 5 }}
-
对输出结果缩进2个空格:
{{ .Values.favorite.food | nindent 2 }}
- 常用的关系运算符
>
、>=
、<
、!=
、与或非在 helm 模版中都以函数的形式实现。
关系运算函数定义:
- eq 相当于
=
- ne 相当于
!=
- lt 相当于
<=
- gt 相当于
>=
- and 相当于
&&
- or 相当于
||
- not 相当于
!
{{ if and .Values.fooString (eq .Values.fooString "foo") }}
{{ ... }}
{{ end }}
// 相当于 if (.Values.fooString && (.Values.fooString == "foo"))
流程控制语句
- if/else
{{ if 条件表达式 }}
# Do something
{{ else if 条件表达式 }}
# Do something else
{{ else }}
# Default case
{{ end }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
{{if eq .Values.favorite.drink "coffee"}}
mug: true
{{end}}
- with
with主要就是用来修改 . 作用域的,默认 . 代表全局作用域,with语句可以修改.的含义.
{{ with 引用的对象 }}